{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "21db97de",
   "metadata": {},
   "source": [
    "# Simple Planar SLAM Example"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "153c8385",
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "source": [
    "GTSAM Copyright 2010-2018, Georgia Tech Research Corporation,\n",
    "Atlanta, Georgia 30332-0415\n",
    "All Rights Reserved\n",
    "Authors: Frank Dellaert, et al. (see THANKS for the full author list)\n",
    "\n",
    "See LICENSE for the license information\n",
    "\n",
    "Simple robotics example using odometry measurements and bearing-range (laser) measurements\n",
    "Author: Alex Cunningham (C++), Kevin Deng & Frank Dellaert (Python)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ced5f44",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "source": [
    "<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/python/gtsam/examples/PlanarSLAMExample.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2980e5e",
   "metadata": {},
   "source": [
    "This notebook demonstrates a basic Simultaneous Localization and Mapping (SLAM) problem in 2D using GTSAM.\n",
    "\n",
    "**What is GTSAM?**\n",
    "GTSAM (Georgia Tech Smoothing and Mapping) is a library that implements factor graph-based optimization. It's widely used in robotics for problems like SLAM, Structure from Motion (SfM), and sensor fusion.\n",
    "\n",
    "**What is a Factor Graph?**\n",
    "A factor graph is a graphical model that represents the probabilistic relationships between unknown variables (like robot poses and landmark positions) and measurements (like odometry or sensor readings). Optimization in GTSAM involves finding the most likely configuration of variables given the measurements and their associated uncertainties (noise).\n",
    "\n",
    "**This Example:**\n",
    "We'll simulate a robot moving in a 2D plane. The robot has:\n",
    "1.  **Odometry:** Measurements of its own motion (how far it moved between steps).\n",
    "2.  **Bearing-Range Sensor:** A sensor (like a simple laser scanner) that measures the bearing (angle) and range (distance) to landmarks.\n",
    "\n",
    "We'll build a factor graph representing the robot's poses, landmark positions, odometry measurements, and bearing-range measurements. Then, we'll use GTSAM to optimize the graph and find the best estimate of the robot's trajectory and landmark locations."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9eb7fe9d",
   "metadata": {},
   "source": [
    "## 1. Setup and Imports\n",
    "\n",
    "First, we need to import the necessary libraries: `gtsam` itself and `numpy` for numerical operations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "eea967e9",
   "metadata": {
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "# Install GTSAM from pip if running in Google Colab\n",
    "try:\n",
    "    import google.colab\n",
    "    %pip install --quiet gtsam-develop\n",
    "except ImportError:\n",
    "    pass # Not in Colab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "2c932acb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import graphviz\n",
    "\n",
    "import gtsam\n",
    "import gtsam.utils.plot as gp\n",
    "\n",
    "# We can use shorthand symbols for variable keys\n",
    "# X(i) represents the i-th pose variable\n",
    "# L(j) represents the j-th landmark variable\n",
    "from gtsam.symbol_shorthand import L, X"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "181c929c",
   "metadata": {},
   "source": [
    "## 2. Define Noise Models\n",
    "\n",
    "Real-world measurements are always noisy. In GTSAM, we model this noise using Gaussian noise models. We need to define the uncertainty (standard deviation) for each type of measurement:\n",
    "\n",
    "*   **Prior Noise:** Uncertainty about the very first pose of the robot. We often assume we know the starting position reasonably well, but not perfectly.\n",
    "*   **Odometry Noise:** Uncertainty in the robot's movement measurements (e.g., wheel encoders). Assumed to be Gaussian noise on the change in x, y, and theta.\n",
    "*   **Measurement Noise:** Uncertainty in the bearing-range sensor readings. Assumed to be Gaussian noise on the bearing and range measurements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4a9b8d1b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create noise models with specified standard deviations (sigmas).\n",
    "# gtsam.noiseModel.Diagonal.Sigmas takes a numpy array of standard deviations.\n",
    "\n",
    "# Prior noise on the first pose (x, y, theta) - sigmas = [0.3m, 0.3m, 0.1rad]\n",
    "PRIOR_NOISE = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.3, 0.3, 0.1]))\n",
    "# Odometry noise (dx, dy, dtheta) - sigmas = [0.2m, 0.2m, 0.1rad]\n",
    "ODOMETRY_NOISE = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.2, 0.2, 0.1]))\n",
    "# Measurement noise (bearing, range) - sigmas = [0.1rad, 0.2m]\n",
    "MEASUREMENT_NOISE = gtsam.noiseModel.Diagonal.Sigmas(np.array([0.1, 0.2]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6202026a",
   "metadata": {},
   "source": [
    "## 3. Build the Factor Graph\n",
    "\n",
    "Now, we'll create the factor graph step-by-step.\n",
    "\n",
    "First, create an empty nonlinear factor graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "37e5a43a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create an empty nonlinear factor graph\n",
    "graph = gtsam.NonlinearFactorGraph()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a1b517b",
   "metadata": {},
   "source": [
    "### 3.1 Variable Keys\n",
    "\n",
    "We need unique keys to identify each unknown variable (robot poses and landmark positions) in the graph. \n",
    "We'll have 3 robot poses $(x_1, x_2, x_3)$ and 2 landmarks $(l_1, l_2)$. We will just use `X(1)`..`L(2)` in the code, using the `X(i)` and `L(j)` shorthand imported earlier. Note here we can use base 1 indexing but you can use any integer, and numbering does not have to be consecutive."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9e1aaaa",
   "metadata": {},
   "source": [
    "### 3.2 Add a Prior Factor\n",
    "\n",
    "To \"anchor\" the graph, we add a prior factor on the first pose $x_1$. This represents our initial belief about where the robot started. We assume it started at the origin (0, 0, 0) with the uncertainty defined by `PRIOR_NOISE`.\n",
    "\n",
    "A `PriorFactorPose2` connects the single pose variable $x_1$ to a known pose (`gtsam.Pose2(0,0,0)`) with a specific noise model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0549b0a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add a prior on pose X(1) at the origin.\n",
    "# A prior factor consists of a mean (gtsam.Pose2) and a noise model.\n",
    "graph.add(gtsam.PriorFactorPose2(X(1), gtsam.Pose2(0.0, 0.0, 0.0), PRIOR_NOISE))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb8ffd2d",
   "metadata": {},
   "source": [
    "### 3.3 Add Odometry Factors\n",
    "\n",
    "Next, we add factors representing the robot's movement based on odometry measurements. We assume the robot moved approximately 2 units forward in the x-direction between each pose.\n",
    "\n",
    "A `BetweenFactorPose2` connects two consecutive poses (e.g., $x_1$ and $x_2$) and represents the measured relative motion between them (`gtsam.Pose2(2.0, 0.0, 0.0)`) with its associated noise (`ODOMETRY_NOISE`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "2389fae3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add odometry factors between X(1),X(2) and X(2),X(3), respectively.\n",
    "# The measurement is the relative motion: Pose2(dx, dy, dtheta).\n",
    "\n",
    "# Between X(1) and X(2): Move forward 2m\n",
    "graph.add(gtsam.BetweenFactorPose2(X(1), X(2), gtsam.Pose2(2.0, 0.0, 0.0), ODOMETRY_NOISE))\n",
    "# Between X(2) and X(3): Move forward 2m\n",
    "graph.add(gtsam.BetweenFactorPose2(X(2), X(3), gtsam.Pose2(2.0, 0.0, 0.0), ODOMETRY_NOISE))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17459992",
   "metadata": {},
   "source": [
    "### 3.4 Add Measurement Factors\n",
    "\n",
    "Now, add factors representing the bearing-range measurements from the robot's poses to the landmarks.\n",
    "\n",
    "A `BearingRangeFactor2D` connects a pose variable (e.g., $x_1$) and a landmark variable (e.g., $l_1$). It includes the measured bearing (`gtsam.Rot2`) and range (distance), along with the measurement noise (`MEASUREMENT_NOISE`).\n",
    "\n",
    "We have three measurements:\n",
    "*   From $x_1$ to $l_1$: Bearing 45 degrees, Range sqrt(8)\n",
    "*   From $x_2$ to $l_1$: Bearing 90 degrees, Range 2.0\n",
    "*   From $x_3$ to $l_2$: Bearing 90 degrees, Range 2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "cc19f4ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Add Range-Bearing measurements to two different landmarks L(1) and L(2).\n",
    "# Measurements are Bearing (gtsam.Rot2) and Range (float).\n",
    "\n",
    "# From X(1) to L(1)\n",
    "graph.add(gtsam.BearingRangeFactor2D(X(1), L(1), gtsam.Rot2.fromDegrees(45), np.sqrt(4.0+4.0), MEASUREMENT_NOISE))\n",
    "# From X(2) to L(1)\n",
    "graph.add(gtsam.BearingRangeFactor2D(X(2), L(1), gtsam.Rot2.fromDegrees(90), 2.0, MEASUREMENT_NOISE))\n",
    "# From X(3) to L(2)\n",
    "graph.add(gtsam.BearingRangeFactor2D(X(3), L(2), gtsam.Rot2.fromDegrees(90), 2.0, MEASUREMENT_NOISE))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4820f7b1",
   "metadata": {},
   "source": [
    "### 3.5 Inspect the Graph\n",
    "\n",
    "We can print the factor graph to see a summary of the variables and factors we've added."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "83b8002e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Factor Graph:\n",
      "NonlinearFactorGraph: size: 6\n",
      "\n",
      "Factor 0: PriorFactor on x1\n",
      "  prior mean:  (0, 0, 0)\n",
      "  noise model: diagonal sigmas [0.3; 0.3; 0.1];\n",
      "\n",
      "Factor 1: BetweenFactor(x1,x2)\n",
      "  measured:  (2, 0, 0)\n",
      "  noise model: diagonal sigmas [0.2; 0.2; 0.1];\n",
      "\n",
      "Factor 2: BetweenFactor(x2,x3)\n",
      "  measured:  (2, 0, 0)\n",
      "  noise model: diagonal sigmas [0.2; 0.2; 0.1];\n",
      "\n",
      "Factor 3: BearingRangeFactor\n",
      "Factor 3:   keys = { x1 l1 }\n",
      "  noise model: diagonal sigmas [0.1; 0.2];\n",
      "ExpressionFactor with measurement: bearing : 0.785398163\n",
      "range  2.82842712\n",
      "\n",
      "Factor 4: BearingRangeFactor\n",
      "Factor 4:   keys = { x2 l1 }\n",
      "  noise model: diagonal sigmas [0.1; 0.2];\n",
      "ExpressionFactor with measurement: bearing : 1.57079633\n",
      "range  2\n",
      "\n",
      "Factor 5: BearingRangeFactor\n",
      "Factor 5:   keys = { x3 l2 }\n",
      "  noise model: diagonal sigmas [0.1; 0.2];\n",
      "ExpressionFactor with measurement: bearing : 1.57079633\n",
      "range  2\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Print the graph. This shows the factors and the variables they connect.\n",
    "print(\"Factor Graph:\\n{}\".format(graph))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "038f5089",
   "metadata": {},
   "source": [
    "## 4. Create Initial Estimate\n",
    "\n",
    "Factor graph optimization is an iterative process that needs an initial guess (initial estimate) for the values of all unknown variables. The closer the initial estimate is to the true solution, the faster and more reliably the optimizer will converge.\n",
    "\n",
    "Here, we create a `gtsam.Values` object and deliberately insert slightly *inaccurate* initial estimates for the poses and landmark positions. This demonstrates that the optimizer can correct for errors in the initial guess."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "98c87675",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initial Estimate:\n",
      "Values with 5 values:\n",
      "Value l1: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)\n",
      "[\n",
      "\t1.8;\n",
      "\t2.1\n",
      "]\n",
      "\n",
      "Value l2: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)\n",
      "[\n",
      "\t4.1;\n",
      "\t1.8\n",
      "]\n",
      "\n",
      "Value x1: (gtsam::Pose2)\n",
      "(-0.25, 0.2, 0.15)\n",
      "\n",
      "Value x2: (gtsam::Pose2)\n",
      "(2.3, 0.1, -0.2)\n",
      "\n",
      "Value x3: (gtsam::Pose2)\n",
      "(4.1, 0.1, 0.1)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Create (deliberately inaccurate) initial estimate.\n",
    "# gtsam.Values is a container mapping variable keys to their estimated values.\n",
    "initial_estimate = gtsam.Values()\n",
    "\n",
    "# Insert initial guesses for poses (Pose2: x, y, theta)\n",
    "initial_estimate.insert(X(1), gtsam.Pose2(-0.25, 0.20, 0.15))\n",
    "initial_estimate.insert(X(2), gtsam.Pose2(2.30, 0.10, -0.20))\n",
    "initial_estimate.insert(X(3), gtsam.Pose2(4.10, 0.10, 0.10))\n",
    "\n",
    "# Insert initial guesses for landmarks (Point2: x, y)\n",
    "initial_estimate.insert(L(1), gtsam.Point2(1.80, 2.10))\n",
    "initial_estimate.insert(L(2), gtsam.Point2(4.10, 1.80))\n",
    "\n",
    "# Print the initial estimate\n",
    "print(\"Initial Estimate:\\n{}\".format(initial_estimate))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d796916",
   "metadata": {},
   "source": [
    "Now that we have an initial estimate we can also visualize the graph:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "d896ecee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 12.0.0 (0)\n",
       " -->\n",
       "<!-- Pages: 1 -->\n",
       "<svg width=\"350pt\" height=\"84pt\"\n",
       " viewBox=\"0.00 0.00 350.00 83.60\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 79.6)\">\n",
       "<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-79.6 346,-79.6 346,4 -4,4\"/>\n",
       "<!-- var7782220156096217089 -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>var7782220156096217089</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"99\" y=\"-52.55\" font-family=\"Times,serif\" font-size=\"14.00\">l1</text>\n",
       "</g>\n",
       "<!-- factor3 -->\n",
       "<g id=\"node9\" class=\"node\">\n",
       "<title>factor3</title>\n",
       "<ellipse fill=\"black\" stroke=\"black\" cx=\"75\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
       "</g>\n",
       "<!-- var7782220156096217089&#45;&#45;factor3 -->\n",
       "<g id=\"edge7\" class=\"edge\">\n",
       "<title>var7782220156096217089&#45;&#45;factor3</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M91.54,-39.87C85.48,-26.3 77.62,-8.68 75.53,-3.99\"/>\n",
       "</g>\n",
       "<!-- factor4 -->\n",
       "<g id=\"node10\" class=\"node\">\n",
       "<title>factor4</title>\n",
       "<ellipse fill=\"black\" stroke=\"black\" cx=\"123\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
       "</g>\n",
       "<!-- var7782220156096217089&#45;&#45;factor4 -->\n",
       "<g id=\"edge9\" class=\"edge\">\n",
       "<title>var7782220156096217089&#45;&#45;factor4</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M106.46,-39.87C112.52,-26.3 120.38,-8.68 122.47,-3.99\"/>\n",
       "</g>\n",
       "<!-- var7782220156096217090 -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>var7782220156096217090</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"315\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"315\" y=\"-52.55\" font-family=\"Times,serif\" font-size=\"14.00\">l2</text>\n",
       "</g>\n",
       "<!-- factor5 -->\n",
       "<g id=\"node11\" class=\"node\">\n",
       "<title>factor5</title>\n",
       "<ellipse fill=\"black\" stroke=\"black\" cx=\"279\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
       "</g>\n",
       "<!-- var7782220156096217090&#45;&#45;factor5 -->\n",
       "<g id=\"edge11\" class=\"edge\">\n",
       "<title>var7782220156096217090&#45;&#45;factor5</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M304.39,-40.75C295.1,-26.86 282.63,-8.22 279.66,-3.78\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352321 -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>var8646911284551352321</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"27\" y=\"-52.55\" font-family=\"Times,serif\" font-size=\"14.00\">x1</text>\n",
       "</g>\n",
       "<!-- factor0 -->\n",
       "<g id=\"node6\" class=\"node\">\n",
       "<title>factor0</title>\n",
       "<ellipse fill=\"black\" stroke=\"black\" cx=\"27\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352321&#45;&#45;factor0 -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>var8646911284551352321&#45;&#45;factor0</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M27,-39.28C27,-25.77 27,-8.54 27,-3.96\"/>\n",
       "</g>\n",
       "<!-- factor1 -->\n",
       "<g id=\"node7\" class=\"node\">\n",
       "<title>factor1</title>\n",
       "<ellipse fill=\"black\" stroke=\"black\" cx=\"100\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352321&#45;&#45;factor1 -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>var8646911284551352321&#45;&#45;factor1</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M44.67,-43.58C64.25,-29.15 93.78,-7.38 99.15,-3.43\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352321&#45;&#45;factor3 -->\n",
       "<g id=\"edge6\" class=\"edge\">\n",
       "<title>var8646911284551352321&#45;&#45;factor3</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M40.37,-41.61C52.8,-27.68 69.99,-8.41 74.09,-3.81\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352322 -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>var8646911284551352322</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"171\" y=\"-52.55\" font-family=\"Times,serif\" font-size=\"14.00\">x2</text>\n",
       "</g>\n",
       "<!-- var8646911284551352322&#45;&#45;factor1 -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>var8646911284551352322&#45;&#45;factor1</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M153.81,-43.58C134.77,-29.15 106.05,-7.38 100.83,-3.43\"/>\n",
       "</g>\n",
       "<!-- factor2 -->\n",
       "<g id=\"node8\" class=\"node\">\n",
       "<title>factor2</title>\n",
       "<ellipse fill=\"black\" stroke=\"black\" cx=\"207\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352322&#45;&#45;factor2 -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>var8646911284551352322&#45;&#45;factor2</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M181.61,-40.75C190.9,-26.86 203.37,-8.22 206.34,-3.78\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352322&#45;&#45;factor4 -->\n",
       "<g id=\"edge8\" class=\"edge\">\n",
       "<title>var8646911284551352322&#45;&#45;factor4</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M157.63,-41.61C145.2,-27.68 128.01,-8.41 123.91,-3.81\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352323 -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>var8646911284551352323</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"243\" y=\"-52.55\" font-family=\"Times,serif\" font-size=\"14.00\">x3</text>\n",
       "</g>\n",
       "<!-- var8646911284551352323&#45;&#45;factor2 -->\n",
       "<g id=\"edge5\" class=\"edge\">\n",
       "<title>var8646911284551352323&#45;&#45;factor2</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M232.39,-40.75C223.1,-26.86 210.63,-8.22 207.66,-3.78\"/>\n",
       "</g>\n",
       "<!-- var8646911284551352323&#45;&#45;factor5 -->\n",
       "<g id=\"edge10\" class=\"edge\">\n",
       "<title>var8646911284551352323&#45;&#45;factor5</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M253.61,-40.75C262.9,-26.86 275.37,-8.22 278.34,-3.78\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.sources.Source at 0x10a2faf30>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(graphviz.Source(graph.dot(initial_estimate)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2608bf96",
   "metadata": {},
   "source": [
    "## 5. Optimize the Factor Graph\n",
    "\n",
    "Now, we use an optimizer to find the variable configuration that best fits all the factors (measurements) in the graph, starting from the initial estimate.\n",
    "\n",
    "We'll use the Levenberg-Marquardt (LM) algorithm, a standard non-linear least-squares optimizer.\n",
    "\n",
    "1.  Create LM parameters (`gtsam.LevenbergMarquardtParams`). We'll use the defaults.\n",
    "2.  Create the optimizer instance, providing the graph, initial estimate, and parameters.\n",
    "3.  Run the optimization by calling `optimizer.optimize()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "2ee6b17a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Final Result:\n",
      "Values with 5 values:\n",
      "Value l1: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)\n",
      "[\n",
      "\t2;\n",
      "\t2\n",
      "]\n",
      "\n",
      "Value l2: (Eigen::Matrix<double, -1, 1, 0, -1, 1>)\n",
      "[\n",
      "\t4;\n",
      "\t2\n",
      "]\n",
      "\n",
      "Value x1: (gtsam::Pose2)\n",
      "(-5.72151617e-16, -2.6221043e-16, -8.93525825e-17)\n",
      "\n",
      "Value x2: (gtsam::Pose2)\n",
      "(2, -5.76036948e-15, -6.89367166e-16)\n",
      "\n",
      "Value x3: (gtsam::Pose2)\n",
      "(4, -1.0618198e-14, -6.48560093e-16)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Optimize using Levenberg-Marquardt optimization.\n",
    "# The optimizer accepts optional parameters, but we'll use the defaults here.\n",
    "params = gtsam.LevenbergMarquardtParams()\n",
    "optimizer = gtsam.LevenbergMarquardtOptimizer(graph, initial_estimate, params)\n",
    "\n",
    "# Perform the optimization\n",
    "result = optimizer.optimize()\n",
    "\n",
    "# Print the final optimized result\n",
    "# This gtsam.Values object contains the most likely estimates for all variables.\n",
    "print(\"\\nFinal Result:\\n{}\".format(result))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1bda91e1",
   "metadata": {},
   "source": [
    "The code below visualizes the optimized poses and landmarks in 2D. It uses GTSAM's plotting utilities to plot the robot's trajectory (poses) and the estimated positions of the landmarks. The poses are represented as coordinate frames indicating the robot's orientation, while the landmarks are plotted as blue points. The aspect ratio is set to ensure equal scaling for both axes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "d827195e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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xsvQ0FCWLl2alStX5sknn8yMGTM+cp/29vasWbNmxLrVq1envb19t9s3NDSkqalpxAKMHX/0R3v/H+PixaM7D5TIcTRSTeNlyZIlefDBB7NixYo0Njamp6cnPT09+fGPfzy8zcKFC9PZ2Tl8+8Ybb8yqVaty55135pVXXslf/MVfZMOGDVm6dGktRwVq5IQTdr0fP25ccsghI3/9yleS44+v94Qw9jmORqrpR6UrezjH9cADD+Tan36u68ILL8z06dOzfPny4a8/8sgj+dznPpfXXnstJ5xwQv7mb/4ml1122T49po9Kw9i0efOuf2Rfe23XKe7Fi3/9/sGFj+tgPo725/V7VL/Py2gQLwBQnjH7fV4AAD4u8QIAFEW8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFAU8QIAFEW8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFAU8QIAFEW8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFAU8QIAFEW8AABFES8AQFHECwBQFPECABSlpvGybt26XHHFFZk6dWoqlUoeffTRvW7/1FNPpVKpfGjp6emp5ZgAQEFqGi/9/f2ZNWtWli1btl/7bdq0KVu3bh1eJk+eXKMJAYDSHFrLO7/00ktz6aWX7vd+kydPzhFHHHHgBwIAijcmr3mZPXt2pkyZkk996lP59re/vddtBwYGUq1WRywAwMFrTMXLlClTcu+99+ZrX/tavva1r6WtrS0XXnhhXnjhhT3u09XVlebm5uGlra1tFCcGAEZbZWhoaGhUHqhSycqVK7NgwYL92u+CCy7Isccem3/+53/e7dcHBgYyMDAwfLtaraatrS19fX1pamr6OCMDAKOkWq2mubl5n16/a3rNy4Fw9tln5+mnn97j1xsaGtLQ0DCKEwEA9TSm3jbane7u7kyZMqXeYwAAY0RNz7xs3749mzdvHr796quvpru7O5MmTcqxxx6bzs7OvPnmm/mnf/qnJMldd92VGTNm5Ld/+7fzk5/8JF/+8pfz5JNP5j//8z9rOSYAUJCaxsuGDRty0UUXDd/u6OhIkixatCjLly/P1q1b88Ybbwx//YMPPsjNN9+cN998MxMnTszMmTPzrW99a8R9AAC/3kbtgt3Rsj8X/AAAY8P+vH6P+WteAAB+kXgBAIoiXgCAoogXAKAo4gUAKIp4AQCKIl4AgKKIFwCgKOIFACiKeAEAiiJeAICiiBcAoCjiBQAoingBAIoiXgCAoogXAKAo4gUAKIp4AQCKIl4AgKKIFwCgKOIFACiKeAEAiiJeAICiiBcAoCjiBQAoingBAIoiXgCAoogXAKAo4gUAKIp4AQCKIl4AgKKIFwCgKOIFACiKeAEAiiJeAICiiBcAoCjiBQAoingBAIoiXgCAoogXAKAo4gUAKEpN42XdunW54oorMnXq1FQqlTz66KMfuc9TTz2VM844Iw0NDTn++OOzfPnyWo4IABSmpvHS39+fWbNmZdmyZfu0/auvvprLL788F110Ubq7u3PTTTfl+uuvzxNPPFHLMQGAghxayzu/9NJLc+mll+7z9vfee29mzJiRO++8M0ly8skn5+mnn87f//3fZ/78+bUaEwAoyJi65mX9+vWZN2/eiHXz58/P+vXr97jPwMBAqtXqiAUAOHiNqXjp6elJS0vLiHUtLS2pVqv58Y9/vNt9urq60tzcPLy0tbWNxqgAQJ2MqXj5VXR2dqavr2942bJlS71HAgBqqKbXvOyv1tbW9Pb2jljX29ubpqamHHbYYbvdp6GhIQ0NDaMxHgAwBoypMy/t7e1Zs2bNiHWrV69Oe3t7nSYCAMaamsbL9u3b093dne7u7iS7Pgrd3d2dN954I8mut3wWLlw4vP1nPvOZ/O///m/+9E//NK+88kq++MUv5l/+5V/yJ3/yJ7UcEwAoSE3jZcOGDTn99NNz+umnJ0k6Ojpy+umn59Zbb02SbN26dThkkmTGjBn5j//4j6xevTqzZs3KnXfemS9/+cs+Jg0ADKsMDQ0N1XuIA6laraa5uTl9fX1pamqq9zgAwD7Yn9fvMXXNCwDARxEvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFAU8QIAFEW8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFAU8QIAFEW8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFAU8QIAFEW8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUUYlXpYtW5bp06dnwoQJmTt3bp577rk9brt8+fJUKpURy4QJE0ZjTACgADWPl4cffjgdHR257bbb8sILL2TWrFmZP39+tm3btsd9mpqasnXr1uHl9ddfr/WYAEAhah4vf/d3f5cbbrgh1113XU455ZTce++9mThxYu6///497lOpVNLa2jq8tLS01HpMAKAQNY2XDz74IBs3bsy8efN+/oDjxmXevHlZv379Hvfbvn17jjvuuLS1teXKK6/Myy+/vMdtBwYGUq1WRywAwMGrpvHyzjvvZOfOnR86c9LS0pKenp7d7nPiiSfm/vvvz2OPPZYHH3wwg4ODOffcc/ODH/xgt9t3dXWlubl5eGlrazvgfw4AYOwYc582am9vz8KFCzN79uxccMEF+frXv56jjz46991332637+zsTF9f3/CyZcuWUZ4YABhNh9byzo866qgccsgh6e3tHbG+t7c3ra2t+3Qfn/jEJ3L66adn8+bNu/16Q0NDGhoaPvasAEAZanrmZfz48ZkzZ07WrFkzvG5wcDBr1qxJe3v7Pt3Hzp078+KLL2bKlCm1GhMAKEhNz7wkSUdHRxYtWpQzzzwzZ599du6666709/fnuuuuS5IsXLgwxxxzTLq6upIkt99+e84555wcf/zxeffdd3PHHXfk9ddfz/XXX1/rUQGAAtQ8Xq666qq8/fbbufXWW9PT05PZs2dn1apVwxfxvvHGGxk37ucngH70ox/lhhtuSE9PT4488sjMmTMnzzzzTE455ZRajwoAFKAyNDQ0VO8hDqRqtZrm5ub09fWlqamp3uMAAPtgf16/x9ynjQAA9ka8AABFES8AQFHECwBQFPECABRFvAAARREvAEBRxAsAUBTxAgAURbwAAEURLwBAUcQLAFCUmv9U6YPF0NBQ3t/xfpJk4icmplKp1HkiKItjCD4+x9Euzrzso/d3vJ/Duw7P4V2HD//FAfadYwg+PsfRLuIFACiKeAEAiiJeAICiiBcAoCjiBQAoingBAIoiXgCAoogXAKAo4gUAKIp4AQCKIl4AgKKIFwCgKOIFACiKeAEAiiJeAICiiBcAoCjiBQAoingBAIoiXgCAoogXAKAo4gUAKIp4AQCKIl4AgKKIFwCgKOIFACiKeAEAiiJeAICijEq8LFu2LNOnT8+E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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(1)\n",
    "axes = fig.add_subplot()\n",
    "axes = fig.axes[0]\n",
    "\n",
    "# Plot 2D poses\n",
    "poses : gtsam.Values = gtsam.utilities.allPose2s(result)\n",
    "for key in poses.keys():\n",
    "    pose = poses.atPose2(key)\n",
    "    gp.plot_pose2_on_axes(axes, pose, axis_length=0.3)\n",
    "\n",
    "# Plot 2D landmarks\n",
    "landmarks : np.ndarray = gtsam.utilities.extractPoint2(result) # 2xn array\n",
    "for landmark in landmarks:\n",
    "    gp.plot_point2_on_axes(axes, landmark, linespec=\"b\")\n",
    "\n",
    "axes.set_aspect(\"equal\", adjustable=\"datalim\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27bc8e38",
   "metadata": {},
   "source": [
    "## 6. Calculate Marginal Covariances\n",
    "\n",
    "Besides finding the optimal values (the mean), GTSAM can also compute the uncertainty (covariance) associated with each variable estimate after optimization. This tells us how confident we are about the estimated poses and landmark locations.\n",
    "\n",
    "We use the `gtsam.Marginals` class to calculate the marginal covariance matrices for each variable.\n",
    "Each pose covariance matrix is a 3x3 matrix because a pose in 2D (`Pose2`) has three components: `(x, y, theta)` Each landmark covariance matrix is a 2x2 matrix because a landmark in 2D is represented by its `(x, y)` position.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "90ef96ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X1 covariance:\n",
      "[[ 9.00000000e-02  4.08945493e-33 -3.19744231e-18]\n",
      " [ 4.08945493e-33  9.00000000e-02 -1.27897692e-17]\n",
      " [-3.19744231e-18 -1.27897692e-17  1.00000000e-02]]\n",
      "\n",
      "X2 covariance:\n",
      "[[ 0.12096774 -0.00129032  0.00451613]\n",
      " [-0.00129032  0.1583871   0.02064516]\n",
      " [ 0.00451613  0.02064516  0.01774194]]\n",
      "\n",
      "X3 covariance:\n",
      "[[0.16096774 0.00774194 0.00451613]\n",
      " [0.00774194 0.35193548 0.05612903]\n",
      " [0.00451613 0.05612903 0.02774194]]\n",
      "\n",
      "L1 covariance:\n",
      "[[ 0.16870968 -0.04774194]\n",
      " [-0.04774194  0.16354839]]\n",
      "\n",
      "L2 covariance:\n",
      "[[ 0.29387097 -0.10451613]\n",
      " [-0.10451613  0.39193548]]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Calculate and print marginal covariances for all variables.\n",
    "# This provides information about the uncertainty of the estimates.\n",
    "marginals = gtsam.Marginals(graph, result)\n",
    "\n",
    "# Print the covariance matrix for each variable\n",
    "print(\"X1 covariance:\\n{}\\n\".format(marginals.marginalCovariance(X(1))))\n",
    "print(\"X2 covariance:\\n{}\\n\".format(marginals.marginalCovariance(X(2))))\n",
    "print(\"X3 covariance:\\n{}\\n\".format(marginals.marginalCovariance(X(3))))\n",
    "print(\"L1 covariance:\\n{}\\n\".format(marginals.marginalCovariance(L(1))))\n",
    "print(\"L2 covariance:\\n{}\\n\".format(marginals.marginalCovariance(L(2))))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "62fd4f35",
   "metadata": {},
   "source": [
    "The code below once again visualizes the optimized poses and landmarks, now with their associated uncertainties (covariances).\n",
    "The covariance ellipses plotted on the graph visually represent the uncertainty in the estimates. Larger ellipses indicate higher uncertainty, while smaller ellipses indicate more confident estimates. \n",
    "\n",
    "The prior is on $x_1$, at the origin, and hence that is the most certain pose, after which uncertainty increases. Note that for poses we only show the uncertainty on translation, although each pose also has an uncertain orientation. The covariance ellipses on the landmarks actually reflect that orientation uncertainty, being oriented the way they are."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "d1f03fee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(2)\n",
    "axes = fig.add_subplot()\n",
    "axes = fig.axes[0]\n",
    "\n",
    "# Plot 2D poses\n",
    "poses = gtsam.utilities.allPose2s(result)\n",
    "for key in poses.keys():\n",
    "    pose = poses.atPose2(key)\n",
    "    covariance = marginals.marginalCovariance(key)\n",
    "\n",
    "    gp.plot_pose2_on_axes(axes, pose, covariance=covariance, axis_length=0.3)\n",
    "\n",
    "# Plot 2D landmarks\n",
    "landmarks: np.ndarray = gtsam.utilities.extractPoint2(result)  # 2xn array\n",
    "for j, landmark in enumerate(landmarks):\n",
    "    gp.plot_point2_on_axes(axes, landmark, linespec=\"b\")\n",
    "    covariance = marginals.marginalCovariance(L(j+1))\n",
    "    gp.plot_covariance_ellipse_2d(axes, landmark, covariance=covariance)\n",
    "\n",
    "axes.set_aspect(\"equal\", adjustable=\"datalim\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74673b3d",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "We have successfully:\n",
    "1.  Defined the structure of a 2D SLAM problem using factors (prior, odometry, measurements) and variables (poses, landmarks).\n",
    "2.  Represented this problem as a `gtsam.NonlinearFactorGraph`.\n",
    "3.  Provided noisy measurements and an inaccurate initial estimate.\n",
    "4.  Used `gtsam.LevenbergMarquardtOptimizer` to find the most likely configuration of poses and landmarks.\n",
    "5.  Calculated the uncertainty (covariance) of the final estimates.\n",
    "\n",
    "This demonstrates the basic workflow of using GTSAM for solving SLAM and other robotics estimation problems."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py312",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
